/* Teach Tool  - Structural Dynamics
 * Copyright (C) 2009: Fabian Gerold, www.fabiangerold.de
 *
 * This file may be licensed under the terms of of the
 * GNU General Public License Version 3 (the "GPL").
 * 
 * Software distributed under the License is distributed 
 * on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either 
 * express or implied. See the GPL for the specific language 
 * governing rights and limitations.
 *
 * You should have received a copy of the GPL along with this 
 * program. If not, go to http://www.gnu.org/licenses/gpl.html
 * or write to the Free Software Foundation, Inc., 
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#include <QTimer>
#include <private/qmath_p.h>
#include "SystemFree.h"
#include "WindowFree.h"
#include "../src_common/Diagram.h"
#include "../src_common/EMSWidget.h"
#include "../src_common/System.h"

const float Pi = 3.14159f;

SystemFree::SystemFree()
{
	connect( &m_timer, SIGNAL( timeout() ), this, SLOT( slotFrame() ) );
	m_timer.start( 10 );
	m_halted = true;
	m_paint = true;
	m_buff_output = false;
	m_k = 5000;
	m_m = 25;
	m_xi = 0.05;
	m_amp = 0;
	m_timestep = 0;
	m_sec = 0;
	m_diag_scale = 1.0f;
	
	m_u1 = 0;
	m_up1 = 0;
	m_upp1 = 0;

	m_txt_out = 0;
	m_stop_output = 0;

	parameter();
}

void SystemFree::parameter()
{
	m_T = 2*Pi*qSqrt( m_m/m_k ); //m_T = 2*Pi*sqrt( m_m/m_k );
	m_f = 1.0/m_T;
	m_omega = 2*Pi*m_f;
	
	//m_omega_d = sqrt( 1 - m_xi*m_xi )*m_omega;
	if( m_xi < 1 ) m_omega_d = sqrt(1 - m_xi*m_xi)*m_omega;
	else m_omega_d = 0.9999;

	m_fi = -atan(1/m_xi);

	emit( signalUpdateParameter( m_T, m_f, m_omega, m_omega_d ) );
}


void SystemFree::slotStart( double amp )
{
	resetTime();
	if( amp != 0 )
		m_amp = amp;
	m_stop_output = 0;

	double max_up = 0;
	double max_upp = 0;

	// pre-calculate max u, up, upp
	for( int i=0; i<1000; i++ )
	{
		step();
		m_timestep++;
		m_sec += 0.01;
		if( fabs( m_up ) > max_up ) max_up = fabs( m_up );
		if( fabs( m_upp ) > max_upp ) max_upp = fabs( m_upp );
	}
	resetTime();

	m_halted = false;
	
	emit( signalStart() );
	emit( signalClearPoints() );
	emit( signalDiagSetMax( m_amp, max_up, max_upp ) );
}

void SystemFree::slotStop()
{
	m_halted = true;
}

void SystemFree::proceed()
{
	m_halted = false;
}

void SystemFree::resetTime()
{
	m_halted = true;
	m_timestep = 0;
	m_sec = 0.0;
	m_u = 0;
	m_u1 = 0;
	m_up = 0;
	m_up1 = 0;
	m_upp = 0;
	m_upp1 = 0;
	emit( signalUpdateTime( m_sec ) );
	emit( signalClearPoints() );
	emit( signalDiagUpdate() );
	emit( signalClearOutput() );
}

float SystemFree::addScale( float scale )
{
	if( m_diag_scale+scale >= 0.4 && m_diag_scale+scale <= 5 )
	{
		m_diag_scale += scale;
	}

	emit( signalDiagSetScale( m_diag_scale ) );
	
	return m_diag_scale;
}

void SystemFree::reset()
{
	m_halted = true;
	m_timestep = 0;
	m_sec = 0.0;
	m_u = 0;
	m_u1 = 0;
	m_up = 0;
	m_up1 = 0;
	m_upp = 0;
	m_upp1 = 0;
	emit( signalClearPoints() );
	emit( signalDiagUpdate() );
	
	emit( signalSetDisp( m_u, 0 ) );
	//m_ems->setDisp( m_u, 0 );

	emit( signalUpdateTime( m_sec ) );
	emit( signalClearOutput() );
}

void SystemFree::setTxtOutput( int out )
{
	m_txt_out = out;
}

void SystemFree::slotFrame()
{
	if( m_halted ) return;

	step();

	// text output
	if( ( m_txt_out == 1 && m_timestep%10 == 0 ) || m_txt_out == 2 )
	{
		if( m_u < 0.0001 && m_up < 0.0001 && m_upp < 0.0001 )
		{
			m_stop_output++;
		}

		if( m_stop_output < 4 )
		{
			QString output = QString::number( m_sec ) + "\t" + QString::number( m_u, 'g', 3 ) + "\t" + QString::number( m_up, 'g', 3 ) + "\t" + QString::number( m_upp, 'g', 3 );
		
			if( m_txt_out == 1 )
			{
				emit( signalAppendOutput( output ) );
			}
			else
			{
				if( m_buff_output )
				{
					emit( signalAppendOutput( m_output_buff ) );
					emit( signalAppendOutput( output ) );
				}
				else 
				{
					m_output_buff = output;
				}
				m_buff_output = !m_buff_output;
			}
		}
	}
	
	emit( signalDiagAddPoint( m_sec, m_u, m_up, m_upp ) );
	
	m_timestep++;
	m_sec += 0.01;

	if( m_paint )
	{
		emit( signalUpdateTime( m_sec ) );
		emit( signalSetDisp( m_u, 0 ) );

		emit( signalDiagUpdate() );
	}
	m_paint = !m_paint;
}

void SystemFree::step()
{
	double e = exp( -m_xi*m_omega_d*m_sec );
	double sinus = sin( m_omega_d*m_sec - m_fi );
	double cosinus = cos( m_omega_d*m_sec - m_fi );
	
	m_u2 = m_u1;
	m_u1 = m_u;
	m_u = m_amp*e*sinus;

	m_up2 = m_up1;
	m_up1 = m_up;
	m_up = m_amp*m_omega_d*e*( -m_xi*sinus + cosinus );

	m_upp2 = m_upp1;
	m_upp1 = m_upp;
	m_upp = m_amp*m_omega_d*m_omega_d*e*( ( m_xi*m_xi-1 )*sinus - 2*m_xi*cosinus );
}

void SystemFree::setInitDisp( double disp )
{
	m_amp = disp;
	m_u = disp;
	m_halted = true;
	m_sec = 0.0;
	emit( signalUpdateTime( m_sec ) );
	//m_ems->setDisp( m_u, 0 );
	emit( signalSetDisp( m_u, 0 ) );

}
void SystemFree::slotSetDragDisp( double disp )
{
	m_amp = disp;
	m_u = disp;
	//m_ems->slotSetDragDisp( m_u );
	emit( signalSetDragDisp( m_u ) );
}

void SystemFree::setSpring( double k )
{
	m_k = k;
	//m_ems->setSpring( k );
	emit( signalSetSpring( k ) );
	parameter();
}

void SystemFree::setMass( double m )
{
	m_m = m;
	//m_ems->setMass( m );
	emit( signalSetMass( m ) );
	parameter();
}

void SystemFree::setDamp( double d )
{
	m_xi = d*0.01;

	emit( signalSetDamp( d ) );
	parameter();
}

void SystemFree::setAmp( double a )
{
	m_amp = a;
}

